Style guide.md
Python script, ASCII text executable
Style Guide for Python Code in the roundabout project
This document provides conventions for the coding style used in the Roundabout project. Rules can be broken -- just try to make the code as readable as you can.
Code should be written for Python 3.10 unless I say otherwise.
Code Layout
Indents and Line Continuation
Keep lines up to 80 characters long. Use 4 spaces for indentation, per level. Do not use tabs, and do not put trailing whitespace.
For parentheses, three styles are accepted:
# No indent. thing = function(arg1, arg2) # Hanging indent. Align to the contents, not to the bracket. thing = function(arg1, arg2 arg3, arg4, arg5) # Full indent. Here only one argument or variable or whatever is allowed per line. # The closing bracket must be at the previous indent level. # Any level is allowed, but it must be a multiple of 4. thing = function( arg1, arg2, arg3, arg4 )
Collections should read like lists, not tables.
fruits = ["apple", "tomato", "pear", "cherry", "plum", "melon", "grape", "aubergine",] fruits = [ "apple", "tomato", "pear", "cherry", "plum", "melon", "grape", "aubergine", ] # Don't! fruits = [ "apple", "tomato", "pear", "cherry", "plum", "melon", "grape", "aubergine", ]
Additionally, for a collection meant to be expanded use a trailing comma.
For long expressions, begin the line with the operator and align the operand with the indentation level. This is not permitted by PEP 8, but it follows mathematics and leads to nicer layouts.
population = (population + births + immigrants - deaths - emigrants)
When the hanging indent could be mistaken for a block, add an empty line.
Backslashes are discouraged, but allowed if it is the only way to write your expression. For example:
really_long_variable_name_hopefully_yours_wont_be_as_long = \ "Really long value."
Blank Line Rules
Two between top-level class or function definitions.
One between local functions or methods.
One is allowed to separate related groups and logical sections.
One at the end of the file.
One below imports.
Imports
Imports should be on separate lines. from
-imports must import all objects on the same line though.
import flask import os from models import User, Post
Imports should be ordered like this:
__future__
statementsone blank line
magic names (
__all__
,__version__
)one blank line
library imports
library
from
-importsone blank line
application imports
application
from
-imports
Wildcard imports are discouraged and prohibited for libraries.
Interior Whitespace
Never insert more than one space around operators and other symbols. Never add spaces just to align lines.
When to use
around the lowest priority operators in an expression, comparisons, assignments, and logical operators, as well as the function annotation arrow
# Do thing += 10 + 2*thing # Don't thing+=10+2 * thing # Acceptable; use your best judgement thing += 10 + 2 * thing
after the colon when defining a single-line clause, or an annotation
after commas or semicolons
after the comment sign
#
When to avoid
when passing keyword arguments or defining argument defaults, unless they are annotated
inside any brackets
# Do function(arg1, arg2) # Don't! function( arg1, arg2 )
after a trailing comma
# Do (0,) # Don't! (0, )
before commas, semicolons or colons
# Do x, y = y, x # Don't! x , y = y , x
around the slice operator
:
before an argument list
# Do function(arg1, arg2) # Don't! function (arg1, arg2)
before the indexing operator
# Do dictionary["key"] = "Hello World!" # Don't! dictionary ["key"] = "Hello World!"
Other
Never use the semicolon for multiple statements; while allowed, it is discouraged as it hurts readability. Similarly discouraged are one-line clauses.
Strings
Only use double quotes like "
for strings: single quotes are harder to spot, and they conflict with the common apostrophe.
For docstrings you should follow PEP 257. Additionally, docstrings should use Markdown to allow for a future tool to convert them to docs. HOWEVER arguments should be described using the ReST syntax, as it is more readable in the code. Other than this, they should be kept plaintext to prevent markup language battles (Python prefers RST, but most use Markdown).
Naming
Identifiers must only use ASCII characters. Abbreviations should be kept to a minimum and it should be
made sure that they are widely used and accepted already.
(id
for identifier
or repo
for repository
is acceptable, but avg
for average
is not). Generally, names should be easy to pronounce.
Class names must use UpperCamelCase
with the first letter of each word capitalised and no underscores.
Other names (function, variable) should use snake_case
with all words lowercase and separated by underscores.
For instance and class methods, only name the first argument self
or cls
, respectively.
To avoid conflicts, append an underscore or use a synonym but do not corrupt the spelling (class_
is better than clss
or klass
or classs
or whatever).
Constants are an exception. They should use UPPER_CASE
with underscores.
Non-public names should be prefixed with an underscore. In case it's really important to not use them, use a double underscore to invoke name mangling.
Comments
Block comments should be complete sentences; line comments don't need to. Write comments in
English and always use a space after the comment sign, as stated above, unless it's an UNIX
interpreter descriptor (#!
) where you should not. Inside block comments, separate paragraphs
with an empty comment, like in Markdown. For a solo sentence, full stops are optional.
Avoid stating the obvious or contradicting the code.
Inline comments must be separated with more than one space from the statement, and they may be aligned.
In comments, never alter the case of identifiers, as it may lead to confusion.
Leaving TODO comments as personal notes is allowed, but they should be removed before merging or a release.
Programming
OOP Guidelines
Do not use getters and setters for class attributes. If you do need to change some other things, use properties.
Prefer overloading the operators; make using your objects as natural and Pythonic as possible.
Exceptions
Make exceptions specific enough, so catching them can be explicit.
Do not use the bare except
clause. Make try
clauses as short as possible to avoid silencing
unrelated bugs.
Other
Comparisons to singletons (True
, False
, None
etc.) should be done with the identity operators
is
and is not
, not with the comparison operators. Use is not
, not not ... is
.
Unless it would be ambiguous, use the implicit truth test to check that numbers are different to 0, that containers have contents and similar tests.
Do not assign lambdas to identifiers. Make a real function instead.
Use with
context managers to ensure resources are properly cleaned up.
Prefer making functions that take arguments and return a value instead of making them directly take global variables or process the information such as writing.
Use the methods startswith()
and endswith()
instead of string slicing to check for prefixes
or suffixes.
To compare types, use isinstance()
instead of the is
operator with type()
(this is one of
my problems with Python, but it's the standard).
Use a proper condition for while
instead of a while True
that break
s.
Use for
loops instead of while
loops when possible, and use Pythonic iteration instead of
C-style iteration.
To call shells, use the subprocess
module instead of os.system()
and use the functions
that allow giving a list of arguments instead of a string, it leads to better security.
Similarly, avoid writing SQL manually, use Alchemy. If you must write SQL manually, be extra careful.
Jinja style guide
Jinja should be written like Python, with the following additions:
Tags should be written as {% <content> %}
and expressions as {{ <content> }}
. That is,
put spaces around the content.
Always indent tag contents, just like you would indent HTML tags! If a tag contains other tags and it wouldn't disrupt whitespace, you should indent the contents.
The filter operator |
should have spaces around it, unless it's in a more complex expression
when it shouldn't.
Translations should always be done with the {% trans %}
tag provided by Babel, not with
gettext()
, _()
or others. No exceptions.
The quoting rules are as in Python, unless it's in an HTML attribute, in which case you should use single quotes, as HTML takes precedence.
HTML style guide
HTML tags should be written in lowercase, with attributes in lowercase as well. Attribute values should be quoted with double quotes. Attribute minimisation is suggested for boolean attributes.
Always indent tag contents with 4 spaces, except in plaintext tags like pre
or textarea
, where
you should not indent.
The tag should be multiple lines if the content is complex. Otherwise, mirror the page layout.
IDs or classes should be written in kebab-case
. Names should be written in snake_case
to
provide better compatibility with Python.
Also, when making custom tags, always use a hyphen in the tag name, to make sure they won't be standardised in the future.
Event attributes are allowed as well, but please keep the JS inside shorter than 64 characters. If you need more make a function in a script tag or a separate file.
CSS style guide
CSS selectors, properties and values should be written in lowercase. Custom properties should be
written in kebab-case
.
Always indent the contents of a ruleset with 4 spaces.
Unlike some other style guides, we do not require each selector after a comma to be on a new line. However, if they're too long, very complex or similar and they benefit from alignment, you should do so.
IDs are preferred over classes when the element only appears once on the page.
Tag selectors are allowed! Style the default widgets as you see fit, because it leads to
cleaner HTML. We also apply a reset stylesheet to make sure the default styles are consistent.
In what scenario would you want an unstyled button in your site? Never! Then why always use
<button class="btn btn-primary">
when it's the only kind of <button>
your site has?
However, provide class-based alternatives for tag styles. For example, Efficient UI styles
button
by default, but it also styles .button
to allow hyperlinks or other elements to look
like buttons. Using both isn't needed though.
Also, selectors can be nested where it makes sense, however the >
selector is preferred over
plain nesting, which is generally discouraged.
Use of fancy counters and data-attributes is allowed, but only for cosmetic purposes. We've got server-side templating, profit from it!
JavaScript style guide
JS should use double quotes for strings, and lowerCamelCase for names, and indenting should be 4 spaces. Otherwise I can't comment, because JS is ugly by nature.
1Style Guide for Python Code in the roundabout project 2===================================================== 3 4This document provides conventions for the coding style used in the Roundabout project. Rules can be broken -- just try to make the code as readable as you can. 5 6Code should be written for Python 3.10 unless I say otherwise. 7 8Code Layout 9----------- 10 11### Indents and Line Continuation 12Keep lines up to 80 characters long. Use 4 spaces for indentation, per level. Do not use tabs, and do not put trailing whitespace. 13 14For parentheses, three styles are accepted: 15~~~python 16# No indent. 17thing = function(arg1, arg2) 18 19# Hanging indent. Align to the contents, not to the bracket. 20thing = function(arg1, arg2 21arg3, arg4, arg5) 22 23# Full indent. Here only one argument or variable or whatever is allowed per line. 24# The closing bracket must be at the previous indent level. 25# Any level is allowed, but it must be a multiple of 4. 26thing = function( 27arg1, 28arg2, 29arg3, 30arg4 31) 32~~~ 33 34Collections should read like lists, not tables. 35~~~python 36fruits = ["apple", "tomato", "pear", "cherry", "plum", "melon", "grape", "aubergine",] 37 38fruits = [ 39"apple", 40"tomato", 41"pear", 42"cherry", 43"plum", 44"melon", 45"grape", 46"aubergine", 47] 48 49# Don't! 50fruits = [ 51"apple", "tomato", "pear", "cherry", 52"plum", "melon", "grape", "aubergine", 53] 54~~~ 55 56Additionally, for a collection meant to be expanded use a trailing comma. 57 58For long expressions, begin the line with the operator and align the operand with the indentation level. 59This is not permitted by PEP 8, but it follows mathematics and leads to nicer layouts. 60~~~python 61population = (population 62+ births 63+ immigrants 64- deaths 65- emigrants) 66~~~ 67 68When the hanging indent could be mistaken for a block, add an empty line. 69 70Backslashes are discouraged, but allowed if it is the only way to write your expression. For 71example: 72 73~~~python 74really_long_variable_name_hopefully_yours_wont_be_as_long = \ 75"Really long value." 76~~~ 77 78### Blank Line Rules 79* Two between top-level class or function definitions. 80* One between local functions or methods. 81* One is allowed to separate related groups and logical sections. 82* One at the end of the file. 83* One below imports. 84 85### Imports 86 87Imports should be on separate lines. `from`-imports must import all objects on the same line though. 88~~~python 89import flask 90import os 91 92from models import User, Post 93~~~ 94 95Imports should be ordered like this: 96* `__future__` statements 97* one blank line 98* magic names (`__all__`, `__version__`) 99* one blank line 100* library imports 101* library `from`-imports 102* one blank line 103* application imports 104* application `from`-imports 105 106Wildcard imports are discouraged and prohibited for libraries. 107 108### Interior Whitespace 109Never insert more than one space around operators and other symbols. Never add spaces just to align lines. 110 111#### When to use 112* around the lowest priority operators in an expression, comparisons, assignments, and logical operators, as well as the function annotation arrow 113~~~python 114# Do 115thing += 10 + 2*thing 116# Don't 117thing+=10+2 * thing 118# Acceptable; use your best judgement 119thing += 10 + 2 * thing 120~~~ 121* after the colon when defining a single-line clause, or an annotation 122* after commas or semicolons 123* after the comment sign `#` 124#### When to avoid 125* when passing keyword arguments or defining argument defaults, unless they are annotated 126* inside any brackets 127~~~python 128# Do 129function(arg1, arg2) 130# Don't! 131function( arg1, arg2 ) 132~~~ 133* after a trailing comma 134~~~python 135# Do 136(0,) 137# Don't! 138(0, ) 139~~~ 140* before commas, semicolons or colons 141~~~python 142# Do 143x, y = y, x 144# Don't! 145x , y = y , x 146~~~ 147* around the slice operator `:` 148* before an argument list 149~~~python 150# Do 151function(arg1, arg2) 152# Don't! 153function (arg1, arg2) 154~~~ 155* before the indexing operator 156~~~python 157# Do 158dictionary["key"] = "Hello World!" 159# Don't! 160dictionary ["key"] = "Hello World!" 161~~~ 162 163#### Other 164Never use the semicolon for multiple statements; while allowed, it is discouraged as it hurts readability. Similarly discouraged are one-line clauses. 165 166Strings 167------- 168 169Only use double quotes like `"` for strings: single quotes are harder to spot, and they conflict with the common apostrophe. 170 171For docstrings you should follow [PEP 257](https://peps.python.org/pep-0257/). Additionally, docstrings should use Markdown to allow for a future tool to convert them to docs. 172HOWEVER arguments should be described using the ReST syntax, as it is more readable in the code. 173Other than this, they should be kept plaintext to prevent markup language battles (Python prefers 174RST, but most use Markdown). 175 176Naming 177------ 178 179Identifiers must only use ASCII characters. Abbreviations should be kept to a minimum and it should be 180made sure that they are widely used and accepted already. 181(`id` for `identifier` or `repo` for `repository` is acceptable, but `avg` for `average` is not). Generally, names should be easy to pronounce. 182 183Class names must use `UpperCamelCase` with the first letter of each word capitalised and no underscores. 184 185Other names (function, variable) should use `snake_case` with all words lowercase and separated by underscores. 186 187For instance and class methods, only name the first argument `self` or `cls`, respectively. 188 189To avoid conflicts, append an underscore or use a synonym but do not corrupt the spelling (`class_` is better than `clss` or `klass` or `classs` or whatever). 190 191Constants are an exception. They should use `UPPER_CASE` with underscores. 192 193Non-public names should be prefixed with an underscore. In case it's really important to not use 194them, use a double underscore to invoke name mangling. 195 196Comments 197-------- 198 199Block comments should be complete sentences; line comments don't need to. Write comments in 200English and always use a space after the comment sign, as stated above, unless it's an UNIX 201interpreter descriptor (`#!`) where you should not. Inside block comments, separate paragraphs 202with an empty comment, like in Markdown. For a solo sentence, full stops are optional. 203Avoid stating the obvious or contradicting the code. 204 205Inline comments must be separated with more than one space from the statement, and they may be 206aligned. 207 208In comments, never alter the case of identifiers, as it may lead to confusion. 209 210Leaving TODO comments as personal notes is allowed, but they should be removed before merging 211or a release. 212 213Programming 214----------- 215 216### OOP Guidelines 217Do not use getters and setters for class attributes. If you do need to change some other things, 218use properties. 219 220Prefer overloading the operators; make using your objects as natural and Pythonic as possible. 221 222### Exceptions 223Make exceptions specific enough, so catching them can be explicit. 224 225Do not use the bare `except` clause. Make `try` clauses as short as possible to avoid silencing 226unrelated bugs. 227 228### Other 229Comparisons to singletons (`True`, `False`, `None` etc.) should be done with the identity operators 230`is` and `is not`, not with the comparison operators. Use `is not`, not `not ... is`. 231 232Unless it would be ambiguous, use the implicit truth test to check that numbers are different to 2330, that containers have contents and similar tests. 234 235Do not assign lambdas to identifiers. Make a real function instead. 236 237Use `with` context managers to ensure resources are properly cleaned up. 238 239Prefer making functions that take arguments and return a value instead of making them directly take 240global variables or process the information such as writing. 241 242Use the methods `startswith()` and `endswith()` instead of string slicing to check for prefixes 243or suffixes. 244 245To compare types, use `isinstance()` instead of the `is` operator with `type()` (this is one of 246my problems with Python, but it's the standard). 247 248Use a proper condition for `while` instead of a `while True` that `break`s. 249 250Use `for` loops instead of `while` loops when possible, and use Pythonic iteration instead of 251C-style iteration. 252 253To call shells, use the `subprocess` module instead of `os.system()` and use the functions 254that allow giving a *list* of arguments instead of a string, it leads to better security. 255 256Similarly, avoid writing SQL manually, use Alchemy. If you must write SQL manually, be extra 257careful. 258 259Jinja style guide 260----------------- 261 262Jinja should be written like Python, with the following additions: 263 264Tags should be written as `{% <content> %}` and expressions as `{{ <content> }}`. That is, 265put spaces around the content. 266 267Always indent tag contents, just like you would indent HTML tags! If a tag contains other tags 268and it wouldn't disrupt whitespace, you should indent the contents. 269 270The filter operator `|` should have spaces around it, unless it's in a more complex expression 271when it shouldn't. 272 273Translations should always be done with the `{% trans %}` tag provided by Babel, not with 274`gettext()`, `_()` or others. No exceptions. 275 276The quoting rules are as in Python, unless it's in an HTML attribute, in which case you should 277use single quotes, as HTML takes precedence. 278 279HTML style guide 280---------------- 281 282HTML tags should be written in lowercase, with attributes in lowercase as well. Attribute values 283should be quoted with double quotes. Attribute minimisation is suggested for boolean attributes. 284 285Always indent tag contents with 4 spaces, except in plaintext tags like `pre` or `textarea`, where 286you should not indent. 287 288The tag should be multiple lines if the content is complex. Otherwise, mirror the page layout. 289 290IDs or classes should be written in `kebab-case`. Names should be written in `snake_case` to 291provide better compatibility with Python. 292 293Also, when making custom tags, always use a hyphen in the tag name, to make sure they won't be 294standardised in the future. 295 296Event attributes are allowed as well, but please keep the JS inside shorter than 64 characters. 297If you need more make a function in a script tag or a separate file. 298 299CSS style guide 300--------------- 301 302CSS selectors, properties and values should be written in lowercase. Custom properties should be 303written in `kebab-case`. 304 305Always indent the contents of a ruleset with 4 spaces. 306 307Unlike some other style guides, we do not require each selector after a comma to be on a new line. 308However, if they're too long, very complex or similar and they benefit from alignment, you should 309do so. 310 311IDs are preferred over classes when the element only appears once on the page. 312 313Tag selectors are **allowed**! Style the default widgets as you see fit, because it leads to 314cleaner HTML. We also apply a reset stylesheet to make sure the default styles are consistent. 315In what scenario would you want an *unstyled* button in your site? Never! Then why always use 316`<button class="btn btn-primary">` when it's the only kind of `<button>` your site has? 317 318However, provide class-based alternatives for tag styles. For example, Efficient UI styles 319`button` by default, but it also styles `.button` to allow hyperlinks or other elements to look 320like buttons. Using both isn't needed though. 321 322Also, selectors can be nested where it makes sense, however the `>` selector is preferred over 323plain nesting, which is generally discouraged. 324 325Use of fancy counters and data-attributes is allowed, but only for cosmetic purposes. We've got 326server-side templating, profit from it! 327 328JavaScript style guide 329---------------------- 330 331JS should use double quotes for strings, and lowerCamelCase for names, and indenting should be 4 spaces. 332Otherwise I can't comment, because JS is ugly by nature. 333